

#ifndef __FE8_H__
#define __FE8_H__
#define irqFuncAddr 0x3003090
#include <stdint.h>

__attribute__((optimize("O0")))
static void _CpuSet(const void*s,void*d,uint32_t mode){
    asm("CpuSet:\n");
    asm("swi 11\n");
    asm("bx lr\n");
}

__attribute__((optimize("O0")))
static void _CpuFastSet(const void*s,void*d,uint32_t mode){
    asm("CpuFastSet:\n");
    asm("swi 12\n");
    asm("bx lr\n");
}

extern void CpuSet(void*s,void*d,uint32_t mode);
extern void CpuFastSet(void*s,void*d,uint32_t mode);

inline static int gba_memcpy(void*d,const void*s,int len){
	uint32_t mode = 0;
	len>>=2;  //除以4
	mode |= 1<<26;	//32位模式
	mode |= (len & 0x1FFFFF);
	mode &= ~(1<<24);  //复制模式
	CpuFastSet(s,d,mode);
	return 0;
}

static int gba_memset(void*d,int v,int len){
    int value = v;
	uint32_t mode = 0;
	len>>=2;  //除以4
	mode |= 1<<26;	//32位模式
	mode |= (len & 0x1FFFFF);
	mode |= (1<<24);  //fill模式
	CpuSet(&value,d,mode);
	return 0;
}

#define	REG_BASE	0x04000000
typedef	unsigned char			u8;		/**< Unsigned 8 bit value	*/
typedef	unsigned short int		u16;	/**< Unsigned 16 bit value	*/
typedef	unsigned int			u32;	/**< Unsigned 32 bit value	*/
typedef	signed char				s8;		/**< Signed 8 bit value	*/
typedef	signed short int		s16;	/**< Signed 16 bit value	*/
typedef	signed int				s32;	/**< Signed 32 bit value	*/
typedef	volatile u8				vu8;	/**< volatile Unsigned 8 bit value	*/
typedef	volatile u16			vu16;	/**< volatile Unigned 16 bit value	*/
typedef	volatile u32			vu32;	/**< volatile Unsigned 32 bit value	*/
typedef	volatile s8				vs8;	/**< volatile Signed 8 bit value	*/
typedef	volatile s16			vs16;	/**< volatile Signed 8 bit value	*/
typedef	volatile s32			vs32;	/**< volatile Signed 8 bit value	*/

typedef enum irqMASKS {
	IRQ_VBLANK	=	(1<<0),		/*!< vertical blank interrupt mask */
	IRQ_HBLANK	=	(1<<1),		/*!< horizontal blank interrupt mask */
	IRQ_VCOUNT	=	(1<<2),		/*!< vcount match interrupt mask */
	IRQ_TIMER0	=	(1<<3),		/*!< timer 0 interrupt mask */
	IRQ_TIMER1	=	(1<<4),		/*!< timer 1 interrupt mask */
	IRQ_TIMER2	=	(1<<5),		/*!< timer 2 interrupt mask */
	IRQ_TIMER3	=	(1<<6),		/*!< timer 3 interrupt mask */
	IRQ_SERIAL	=	(1<<7),		/*!< serial interrupt mask */
	IRQ_DMA0	=	(1<<8),		/*!< DMA 0 interrupt mask */
	IRQ_DMA1	=	(1<<9),		/*!< DMA 1 interrupt mask */
	IRQ_DMA2	=	(1<<10),	/*!< DMA 2 interrupt mask */
	IRQ_DMA3	=	(1<<11),	/*!< DMA 3 interrupt mask */
	IRQ_KEYPAD	=	(1<<12),	/*!< Keypad interrupt mask */
	IRQ_GAMEPAK	=	(1<<13)		/*!< horizontal blank interrupt mask */
} irqMASK;

#define	REG_IME		*(vu16 *)(REG_BASE + 0x208)	// Interrupt Master Enable
#define	REG_IE		*(vu16 *)(REG_BASE + 0x200)	// Interrupt Enable
#define	REG_DISPSTAT	*((vu16 *)(REG_BASE + 0x04))

//---------------------------------------------------------------------------------
// LCDC Interrupt bits
//---------------------------------------------------------------------------------
enum LCDC_IRQ {
	LCDC_VBL_FLAG	=	(1<<0),
	LCDC_HBL_FLAG	=	(1<<1),
	LCDC_VCNT_FLAG	=	(1<<2),
	LCDC_VBL		=	(1<<3),
	LCDC_HBL		=	(1<<4),
	LCDC_VCNT		=	(1<<5)
};

static void irqEnable ( int mask ){
   	REG_IME	= 0;

	if (mask & IRQ_VBLANK) REG_DISPSTAT |= LCDC_VBL;
	if (mask & IRQ_HBLANK) REG_DISPSTAT |= LCDC_HBL;
	if (mask & IRQ_VCOUNT) REG_DISPSTAT |= LCDC_VCNT;
	REG_IE |= mask;
	REG_IME	= 1;
}

static irqDisable(int mask) {
//---------------------------------------------------------------------------------
	REG_IME	= 0;

	if (mask & IRQ_VBLANK) REG_DISPSTAT &= ~LCDC_VBL;
	if (mask & IRQ_HBLANK) REG_DISPSTAT &= ~LCDC_HBL;
	if (mask & IRQ_VCOUNT) REG_DISPSTAT &= ~LCDC_VCNT;
	REG_IE &= ~mask;

	REG_IME	= 1;

}

typedef void ( * IntFn)(void);

static IntFn irqSet(irqMASK mask, IntFn function) {
//---------------------------------------------------------------------------------
	int i;
    IntFn *IntrTable = (IntFn *)irqFuncAddr;

	for	(i=0;;i++) {
        if(mask&(1<<i)){
            break;
        }
	}
    IntFn old = IntrTable[i];
    IntrTable[i] = function;
	return old;

}

#define	REG_VCOUNT		*((vu16 *)(REG_BASE + 0x06))


typedef uint32_t (*callT)();

#define CALLT(addr,...) ((callT)(addr+1))(__VA_ARGS__)
#define CALL(addr,...) ((callT)(addr))(__VA_ARGS__)

#define str(reg,v) *((uint32_t*)(reg)) =  (uint32_t)(v)
#define strh(reg,v) *((uint16_t*)(reg)) = (uint16_t)(v)
#define strb(reg,v) *((uint8_t*)(reg)) =  (uint8_t)(v)

#define get(reg) *((uint32_t*)(reg))
#define geth(reg) *((uint16_t*)(reg))
#define getb(reg) *((uint8_t*)(reg))

#endif

